#include "ProcessInfo.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <thread>
#include <assert.h>
#include <dirent.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <algorithm>

#include <iostream>

__thread int t_cachedTid = 0;

__thread int t_numOpenedFiles = 0;

int fdDirFilter(const struct dirent* d)
{
    if (::isdigit(d->d_name[0]))
    {
        ++t_numOpenedFiles;
    }
    return 0;
}

__thread std::vector<pid_t>* t_pids = NULL;
int taskDirFilter(const struct dirent* d)
{
    if (::isdigit(d->d_name[0]))
    {
        t_pids->push_back(atoi(d->d_name));
    }
    return 0;
}

int scanDir(const char *dirpath, int (*filter)(const struct dirent *))
{
    struct dirent** namelist = NULL;
    int result = ::scandir(dirpath, &namelist, filter, alphasort);
    assert(namelist == NULL);
    return result;
}

void readFile(std::string filename, int maxSize, std::string* content) {
    char buf[64*1024];
    int fd = ::open(filename.c_str(), O_RDONLY | O_CLOEXEC);
    content->clear();
    while (content->size() < static_cast<size_t>(maxSize)) {
        size_t toRead = std::min(static_cast<size_t>(maxSize) - content->size(), sizeof(buf));
        ssize_t n = ::read(fd, buf, toRead);
        if (n > 0) {
            // content
            std::string tmp = buf;
            *content += tmp;
        }
        else {
            if (n < 0) {
                // error
            }
            break;
        }
    }
}

std::string ProcessInfo::procStatus() {
    std::string result;
    // std::string filename = "";
    readFile("/proc/self/status", 65536, &result);
    return result;
}

std::string ProcessInfo::procStat() {
    std::string result;
    readFile("/proc/self/stat", 65536, &result);
    return result;
}

std::string ProcessInfo::threadStat()
{
    std::string result;
    // std::thread::id this_id = std::this_thread::get_id();
    // unsigned int* t = (unsigned int*)&this_id;
    // unsigned int threadId = *t;
    t_cachedTid = static_cast<pid_t>(::syscall(SYS_gettid));
    std::string filename = "/proc/self/task/" + std::to_string(t_cachedTid) + "/stat";
    readFile(filename, 65536, &result);

    return result;
}

std::string ProcessInfo::exePath()
{
    std::string result;
    char buf[1024];
    ssize_t n = ::readlink("/proc/self/exe", buf, sizeof(buf));
    if (n > 0)
    {
        // result.assign(buf, n);
        result += std::string(buf);
    }
    return result;
}

int ProcessInfo::openedFiles()
{
    t_numOpenedFiles = 0;
    scanDir("/proc/self/fd", fdDirFilter);
    return t_numOpenedFiles;
}

int ProcessInfo::maxOpenFiles()
{
    struct rlimit rl;

    // 成功时返回0
    if (::getrlimit(RLIMIT_NOFILE, &rl)) {
        return openedFiles();
    }
    else {
        return static_cast<int>(rl.rlim_cur);
    }
}

int ProcessInfo::numThreads()
{
    int result = 0;
    std::string status = procStatus();
    size_t pos = status.find("Threads:");
    if (pos != std::string::npos)
    {
        result = ::atoi(status.c_str() + pos + 8);
    }
    return result;
}

std::vector<pid_t> ProcessInfo::threads()
{
    std::vector<pid_t> result;
    t_pids = &result;
    scanDir("/proc/self/task", taskDirFilter);
    t_pids = NULL;
    std::sort(result.begin(), result.end());
    return result;
}









